//出处：https://space.bilibili.com/3546635661478471
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QBuffer>
#include <QStyle>

#define KEY_ORIG 0
#define KEY_16 16
#define KEY_32 32
#define KEY_48 48
#define KEY_64 64
#define KEY_128 128
#define KEY_256 256

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    m_smooth = Qt::FastTransformation;
    ui->setupUi(this);
    ui->btn_d0->setIcon(qApp->style()->standardIcon(QStyle::SP_DialogCancelButton));
    ui->btn_d16->setIcon(qApp->style()->standardIcon(QStyle::SP_DialogCancelButton));
    ui->btn_d32->setIcon(qApp->style()->standardIcon(QStyle::SP_DialogCancelButton));
    ui->btn_d48->setIcon(qApp->style()->standardIcon(QStyle::SP_DialogCancelButton));
    ui->btn_d64->setIcon(qApp->style()->standardIcon(QStyle::SP_DialogCancelButton));
    ui->btn_d128->setIcon(qApp->style()->standardIcon(QStyle::SP_DialogCancelButton));
    ui->btn_d256->setIcon(qApp->style()->standardIcon(QStyle::SP_DialogCancelButton));
}

MainWindow::~MainWindow()
{
    clearFrame();
    delete ui;
}

///
/// \brief MainWindow::clearFrame 清楚内存中的图像数据，释放内存
///
void MainWindow::clearFrame()
{
    qDeleteAll(m_frame.begin(), m_frame.end());
    m_frame.clear();
}

///
/// \brief MainWindow::outMsg 输出提示信息
/// \param msg 文本内容
/// \param succeed 提示信息是否代表成功选择文本颜色，true=蓝色，false=红色
///
void MainWindow::outMsg(const QString &msg, bool succeed)
{
    ui->lb_msg->setText(msg);
    if(!msg.isEmpty())
    {
        QString cr;
        if(succeed) cr = "color:#0066ff;";
        else cr = "color:#ff6600;";
        ui->lb_msg->setStyleSheet(cr);
    }
}

///
/// \brief MainWindow::loadImg 从磁盘加载一个png文件
/// \param lb_preview 用于预览的label控件
/// \param lb_size 用于显示图像大小的label控件
/// \param key 用于在hash表中存放的键
/// \return
///
bool MainWindow::loadImg(QLabel *lb_preview, QLabel *lb_size, int key)
{

    outMsg("", true);
    QString path = QFileDialog::getOpenFileName(this, tr("请选择png图片"), "", "png(*.png)");
    if(path.isEmpty()) return false;


    QImage img;
    bool b = img.load(path, "PNG");
    if(!b)
    {
        outMsg("加载图像失败", false);
    }
    else
    {
        //保存图像在内存并预览显示
        m_input.insert(key, img);
        lb_size->setText(QString("%1x%2").arg(img.width()).arg(img.height()));
        lb_preview->setPixmap(QPixmap::fromImage(img.scaled(lb_preview->size(), Qt::IgnoreAspectRatio, m_smooth)));
    }
    return b;
}

///
/// \brief MainWindow::delImg 移除已经加载的图像
/// \param lb_preview 用于预览的label控件
/// \param lb_size 用于显示图像大小的label控件
/// \param tb 移除按钮
/// \param key hash表中的key
///
void MainWindow::delImg(QLabel *lb_preview, QLabel *lb_size, QToolButton *tb, int key)
{
    lb_preview->clear();
    lb_size->clear();
    m_input.remove(key);
    tb->setEnabled(false);
}

///
/// \brief MainWindow::getFrame 根据复选框状态，生成或跳过指定大小的帧
/// \param chk checkBox控件
/// \param frames 存放图像帧的列表，存入的图像已缩放到指定大小
/// \param size 图像大小，同时也是hash中的key
/// \return 出现错误返回false，成功或未选择此大小返回true
///
bool MainWindow::getFrame(QCheckBox *chk, QList<QImage> &frames, int size)
{
    if(!chk->isChecked()) return true;

    //没有单独指定帧图像时，使用原始图像
    QImage src = m_input.value(size);
    if(src.isNull()) src = m_input.value(KEY_ORIG);

    if(src.isNull())
    {
        outMsg(tr("帧“%1”没有输入图像").arg(size), false);
        return false;
    }

    if((src.width() < size || src.height() < size) && (!ui->chk_zoomIn->isChecked()))
    {
        outMsg(tr("帧%1输入图像太小").arg(size), false);
        return false;
    }

    if(src.width() != size || src.height() != size)
    {
        src = src.scaled(size, size, Qt::IgnoreAspectRatio, m_smooth);
    }

    frames.append(src);

    return true;
}

///
/// \brief MainWindow::saveIco 将帧图像列表中的图像生成为ico文件
/// \param images 图像列表
/// \param filePath 要保存的文件路径
///
void MainWindow::saveIco(const QList<QImage> &images, const QString &filePath)
{
    QList<QByteArray *> lst;
    QByteArray ico;
    QByteArray *ar;
    QBuffer buffer;

    if(images.isEmpty())
    {
        outMsg(tr("图像列表为空"), false);
    }
    Q_ASSERT(!filePath.isEmpty());
    if(filePath.isEmpty()) return;

    clearFrame();   //释放旧数据

    //每帧图像生成ico格式
    bool b;
    foreach (const QImage &image, images)
    {
        if(!image.isNull())
        {
            ar = new QByteArray();
            buffer.setBuffer(ar);
            b = buffer.open(QIODevice::WriteOnly);
            if(!b)
            {
                outMsg("unkonw err.", false);
                return;
            }
            qDebug("save w=%d", image.width());
            b = image.save(&buffer, "ICO");
            if(!b)
            {
                outMsg("unkonw err 2.", false);
                return;
            }

            buffer.close();
            lst.append(ar);
        }
    }

    if(lst.isEmpty())
    {
        outMsg(tr("没有有效的帧图像"), false);
        return ;
    }

    // //////// 合并为一个ico文件
    //文件头6字节
    const int hdr_len = 6;
    char hdr[hdr_len] = {0};
    hdr[2] = 1; //1=图标，2=光标
    hdr[4] = lst.count();
    ico.append(hdr, hdr_len);

    //每帧图像的信息块16字节
    const int msg_len = 16;
    int offset = hdr_len + msg_len * lst.count();
    for(int i=0; i<lst.count(); i++)
    {
        ar = lst.at(i);
        ico.append(&ar->constData()[hdr_len], msg_len - 4);
        ico.append((char *)&offset, 4); //图像数据的偏移量
        offset += ar->size() - hdr_len - msg_len;
    }

    //每帧图像的数据
    offset = hdr_len + msg_len;
    for(int i=0; i<lst.count(); i++)
    {
        ar = lst.at(i);
        ico.append(&ar->constData()[offset], ar->size() - offset);
    }

    //保存到磁盘
    QFile file(filePath);
    if (file.open(QIODevice::WriteOnly))
    {
        file.write(ico);
        file.close();
        outMsg(tr("保存成功。"), true);
    }
    else
    {
        outMsg(tr("打开文件失败：") + file.errorString(), false);
    }
}

void MainWindow::on_btn_open_clicked()
{
    ui->btn_d0->setEnabled(loadImg(ui->lb_preview, ui->lb_size0, KEY_ORIG));
}

void MainWindow::on_btn_save_clicked()
{
    outMsg("", true);

    //根据复选框，生成帧 [注意256必须放在第一个位置，否则win10资源管理器选超大图标显示的仍是小图标]
    QList<QImage> lst;
    if(!getFrame(ui->chk_256, lst, KEY_256)) return;
    if(!getFrame(ui->chk_128, lst, KEY_128)) return;
    if(!getFrame(ui->chk_64, lst, KEY_64)) return;
    if(!getFrame(ui->chk_48, lst, KEY_48)) return;
    if(!getFrame(ui->chk_32, lst, KEY_32)) return;
    if(!getFrame(ui->chk_16, lst, KEY_16)) return;
    if(lst.isEmpty())
    {
        outMsg(tr("至少要选择一帧"), false);
        return;
    }

    QString path = QFileDialog::getSaveFileName(this, tr(""), "", "ico(*.ico)");
    if(path.isEmpty()) return;

    //保存至文件
    saveIco(lst, path);
}


void MainWindow::on_btn_16_clicked()
{
    ui->btn_d16->setEnabled(loadImg(ui->lb_16, ui->lb_size16, KEY_16));
}


void MainWindow::on_btn_32_clicked()
{
    ui->btn_d32->setEnabled(loadImg(ui->lb_32, ui->lb_size32, KEY_32));
}


void MainWindow::on_btn_48_clicked()
{
    ui->btn_d48->setEnabled(loadImg(ui->lb_48, ui->lb_size48, KEY_48));
}


void MainWindow::on_btn_64_clicked()
{
    ui->btn_d64->setEnabled(loadImg(ui->lb_64, ui->lb_size64, KEY_64));
}


void MainWindow::on_btn_128_clicked()
{
    ui->btn_d128->setEnabled(loadImg(ui->lb_128, ui->lb_size128, KEY_128));
}


void MainWindow::on_btn_256_clicked()
{
    ui->btn_d256->setEnabled(loadImg(ui->lb_256, ui->lb_size256, KEY_256));
}


void MainWindow::on_btn_d0_clicked()
{
    delImg(ui->lb_preview, ui->lb_size0, ui->btn_d0, KEY_ORIG);
}


void MainWindow::on_btn_d16_clicked()
{
    delImg(ui->lb_16, ui->lb_size16, ui->btn_d16, KEY_16);
}


void MainWindow::on_btn_d32_clicked()
{
    delImg(ui->lb_32, ui->lb_size32, ui->btn_d32, KEY_32);
}


void MainWindow::on_btn_d48_clicked()
{
    delImg(ui->lb_48, ui->lb_size48, ui->btn_d48, KEY_48);
}


void MainWindow::on_btn_d64_clicked()
{
    delImg(ui->lb_64, ui->lb_size64, ui->btn_d64, KEY_64);
}


void MainWindow::on_btn_d128_clicked()
{
    delImg(ui->lb_128, ui->lb_size128, ui->btn_d128, KEY_128);
}


void MainWindow::on_btn_d256_clicked()
{
    delImg(ui->lb_256, ui->lb_size256, ui->btn_d256, KEY_256);
}


void MainWindow::on_chk_smooth_clicked(bool checked)
{
    if(checked)
        m_smooth = Qt::SmoothTransformation;
    else
        m_smooth = Qt::FastTransformation;
}

